#ifndef GOAL_H
#define GOAL_H

struct Message;
#include "Vector2D.h"
#include "Goal_Types.h"
#include "GenericKeys.h"
#include "ClippingPlane.h"
#include "GameWorld.h"

template <class entity_type>
class Goal
{
public:
	enum {active, inactive, completed, failed};

protected:
	// An enumerated type specifying the type of goal
	int m_iType;

	// A pointer to the entity that owns this goal
	entity_type* m_pOwner;

	// An enumerated value indicating the goal's status (active, inactive, completed, failed)
	int m_iStatus;

	/*	The following methods were created to factor out some of the commonality
		in the implementations of the Process method. */

	// If m_iStatus = inactive this method sets it to active and calls Activate()
	void ActivateIfInactive();

	// If m_iStatus is failed this method sets it to inactive so that the goal
	// will be reactivated (and therefore re-planned) on the next update-step.
	void ReactivateIfFailed();

public:
	Goal(entity_type* pE, int type):m_iType(type),
									m_pOwner(pE),
									m_iStatus(inactive)
	{}

	virtual ~Goal() {}

	// logic to run when the goal is activated
	virtual void Activate() = 0;

	  //logic to run each update-step
  virtual int  Process() = 0;

  //logic to run when the goal is satisfied. (typically used to switch
  //off any active steering behaviors)
  virtual void Terminate() = 0;

  //goals can handle messages. Many don't though, so this defines a default
  //behavior
  virtual bool HandleMessage(const Message& msg){return false;}


  //a Goal is atomic and cannot aggregate subgoals yet we must implement
  //this method to provide the uniform interface required for the goal
  //hierarchy.
  virtual void AddSubgoal(Goal<entity_type>* g)
  {throw std::runtime_error("Cannot add goals to atomic goals");}


  bool         isComplete()const{return m_iStatus == completed;} 
  bool         isActive()const{return m_iStatus == active;}
  bool         isInactive()const{return m_iStatus == inactive;}
  bool         hasFailed()const{return m_iStatus == failed;}
  int          GetType()const{return m_iType;}

  
  
  //this is used to draw the name of the goal at the specific position
  //used for debugging
  virtual void RenderAtPos(Vector2D& pos)const;
  
  //used to render any goal specific information
  virtual void Render(){}
};



//if m_iStatus is failed this method sets it to inactive so that the goal
//will be reactivated (replanned) on the next update-step.
template <class entity_type>
void  Goal<entity_type>::ReactivateIfFailed()
{
  if (hasFailed())
  {
     m_iStatus = inactive;
  }
}

void Goal<Character>::ReactivateIfFailed();
  
template <class entity_type>
void  Goal<entity_type>::ActivateIfInactive()
{
  if (isInactive())
  {
    Activate();   
  }
}

void Goal<Character>::ActivateIfInactive();

template <class entity_type>
void Goal<entity_type>::RenderAtPos(Vector2D& pos)const
{
	switch(GetType())
	{
	case goal_obey:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_obey", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_obey", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_obey", blue);
			break;
		}
	case goal_move_in_group:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_move_in_group", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_move_in_group", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_move_in_group", blue);
			break;
		}
	case goal_follow_character:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_follow_character", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_follow_character", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_follow_character", blue);
			break;
		}
	case goal_arrive_at_destination:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_arrive_at_destination", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_arrive_at_destination", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_arrive_at_destination", blue);
			break;
		}
	case goal_lead_group:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_leading_group", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_leading_group", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_leading_group", blue);
			break;
		}
	case goal_take_path_to_location:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_take_path_to_location", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_take_path_to_location", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_take_path_to_location", blue);
			break;
		}

	case goal_construct_building:
		{
			if(isComplete())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_construct_building", black);
			if(isActive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_construct_building", red);
			if(isInactive())
				Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "goal_construct_building", blue);
			break;
		}
	default:
		{
			Key->GetFont("onuava")->drawText(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), "default", white);
			break;
		}
	}
}

#endif